package com.one.tank;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

public class GamePanel extends JPanel implements KeyListener {
    //游戏状态常量，0-未开始，1-进行中，9-已结束
    private final static int GAME_STATE_INIT = 0;
    private final static int GAME_STATE_PLAYING = 1;
    private final static int GAME_STATE_OVER = 9;
    //游戏运行场景范围常量
    public final static int GAME_WIDTH = Block.BLOCK_WIDTH * 26;
    public final static int GAME_HEIGHT = Block.BLOCK_HEIGHT * 26;
    //游戏面板范围常量
    public final static int GAME_PANEL_WIDTH = GAME_WIDTH + 150;
    public final static int GAME_PANEL_HEIGHT = GAME_HEIGHT;
    //利用双缓冲机制防止画面闪烁（创建一张与面板画面一样大小的图片，所有的元素先绘制到该图片上，再将该图片一次性绘制到面板画面上）
    private BufferedImage bufferedImage = new BufferedImage(GAME_WIDTH, GAME_HEIGHT, BufferedImage.TYPE_4BYTE_ABGR);
    //游戏刷新的频率-->游戏每秒刷新（1000/repaintRate）次
    private int repaintRate = 50;
    //游戏状态
    public int state = GAME_STATE_INIT;
    //我方坦克
    private OurTank ourTank;
    //我方坦克模型-->显示在计分榜上面
    private OurTank ourTankModel = new OurTank(GAME_WIDTH + 30, 200, Tank.RIGHT);
    //敌方坦克池（考虑线程安全用Vector，没用ArrayList）
    private Vector<EnemyTank> enemyPool = new Vector<>();
    //敌方坦克池的最大数量
    private int enemyPoolMaxNum = 0;
    //敌方坦克死亡数量
    private int enemyDeadNum = 0;
    //敌方坦克模型-->显示在计分榜上面
    private EnemyTank enemyTankModel = new EnemyTank(GAME_WIDTH + 30, 20, Tank.RIGHT);
    //创建敌方坦克线程是否运行标识
    private boolean enemyTankThreadIsRunning = false;
    //上次投放时间-->用来设置投放坦克最小时间间隔
    private long lastEnemyTankTime = System.currentTimeMillis();
    //块列表
    private List<Block> blockList = new ArrayList<>();
    //爆炸列表
    private List<Bomb> bombList = new ArrayList<>();
    //游戏关卡
    private int grade = 1;
    //游戏某关的地图
    private byte[][] gameGradeMap;
    //大本营
    private Camp camp = new Camp(12 * Block.BLOCK_WIDTH, 24 * Block.BLOCK_HEIGHT);

    public GamePanel() {
        //设置面板大小
        this.setSize(GAME_PANEL_WIDTH, GAME_PANEL_HEIGHT);
        setBackground(Color.BLACK);
        //给面板添加键盘监听器
        //让面板获取焦点
        this.setFocusable(true);
        this.addKeyListener(this);
        //创建游戏逻辑实例
        GameLogic.setInstance(new GameLogic(this));
    }
    //绘画，绘制游戏画面（利用双缓冲机制防止画面闪烁）
    @Override
    public void paint(Graphics g) {
        //完成初始化工作，该语句千万不要动
        super.paint(g);
        //准备开始游戏
        if (this.state == GAME_STATE_INIT) {
            //清空之前的内容
            g.setColor(Color.BLACK);
            g.fillRect(0, 0, this.getWidth(), this.getHeight());
            //设置画笔颜色
            g.setColor(Color.ORANGE);
            //设置字体
            g.setFont(new Font("微软雅黑", Font.BOLD, 60));
            //设置一个字符串
            g.drawString("坦  克  大  战", 160, 170);
            g.setColor(Color.CYAN);
            g.drawString("2024", 260, 280);
            g.setColor(Color.WHITE);
            g.setFont(new Font("宋体", Font.BOLD, 20));
            g.drawString("欢 迎 进 入", 280, 360);
            return;
        }
        //游戏进行中
        if (this.state == GAME_STATE_PLAYING) {
            //设置游戏面板区域
            g.setColor(Color.LIGHT_GRAY);
            g.fillRect(0, 0, this.getWidth(), this.getWidth());
            g.setColor(Color.WHITE);
            g.fill3DRect(GAME_WIDTH, 0, 2, GAME_HEIGHT, true);
            //绘制分数
            //敌方坦克信息
            enemyTankModel.drawTank(g);
            g.setColor(Tank.ENEMY_TANK_COLOR);
            g.setFont(new Font("微软雅黑", Font.BOLD, 20));
            g.drawString("总数：" + enemyPoolMaxNum, enemyTankModel.getX(), enemyTankModel.getY() + 70);
            g.drawString("死亡：" + enemyDeadNum, enemyTankModel.getX(), enemyTankModel.getY() + 100);
            g.drawString("剩余：" + (enemyPoolMaxNum - enemyDeadNum), enemyTankModel.getX(), enemyTankModel.getY() + 130);
            //我方坦克信息
            ourTankModel.drawTank(g);
            g.setColor(Tank.OUR_TANK_COLOR);
            g.setFont(new Font("微软雅黑", Font.BOLD, 20));
            g.drawString("总数：1", ourTankModel.getX(), ourTankModel.getY() + 70);
            g.drawString("生命：" + ourTank.getBlood(), ourTankModel.getX(), ourTankModel.getY() + 100);
            //关卡信息
            g.setColor(Color.BLACK);
            int gradeModelX = ourTankModel.getX();
            int gradeModelY = ourTankModel.getY() + 150;
            g.fillRect(gradeModelX, gradeModelY, 3, 40);
            //设置三角形旗帜
            Polygon triangle = new Polygon();
            triangle.addPoint(gradeModelX + 3, gradeModelY);
            triangle.addPoint(gradeModelX + 3, gradeModelY + 20);
            triangle.addPoint(gradeModelX + 40, gradeModelY + 20);
            //设置旗帜的颜色
            g.setColor(Color.RED);
            g.fillPolygon(triangle);
            //设置字体颜色
            g.setColor(Color.WHITE);
            g.setFont(new Font("微软雅黑", Font.BOLD, 20));
            g.drawString("关数：" + this.grade, gradeModelX, gradeModelY + 70);
            //利用双缓冲机制来重绘画面，防止画面闪烁（先把所有的元素绘制到缓冲图片上，再将该图片一次性绘制到画面上）
            //得到缓冲图片的画笔
            Graphics ig = bufferedImage.getGraphics();
            //设置游戏面板区域
            ig.setColor(Color.BLACK);
            ig.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
            //绘制地图
            for (Block block : this.blockList) {
                block.drawBlock(ig);
            }
            //绘制大本营
            if (camp.getState() == Camp.CAMP_STATE_RUNNING) {
                camp.drawCamp(ig);
            }
            //绘制我方坦克
            ourTank.drawTank(ig);
            //绘制我方子弹
            for (Bullet bullet : ourTank.getBullets()) {
                if (bullet.getState() == Bullet.BULLET_STATE_RUNNING) {
                    bullet.drawBullet(ig);
                }
            }
            //绘制敌方坦克，子弹
            for (int i = 0; i < enemyPool.size(); i++) {
                EnemyTank enemyTank = enemyPool.get(i);
                //绘制坦克
                if (enemyTank.getState() == Tank.TANK_GO) {
                    enemyTank.drawTank(ig);
                }
                //绘制子弹
                for (Bullet bullet : enemyTank.getBullets()) {
                    if (bullet.getState() == Bullet.BULLET_STATE_RUNNING) {
                        bullet.drawBullet(ig);
                    }
                }
            }
            //绘制爆炸
            for (Bomb bomb : bombList) {
                if (bomb.getState() == Bomb.BOMB_STATE_RUNNING) {
                    bomb.drawBomb(ig);
                }
            }
            //将缓冲图片一次性显示到画面上
            g.drawImage(bufferedImage, 0, 0, null);

        }
        if (this.state == GAME_STATE_OVER) {
            g.setColor(Color.BLACK);
            g.fillRect(0, 0, this.getWidth(), this.getHeight());
            g.setColor(Color.RED);
            g.setFont(new Font("微软雅黑", Font.BOLD, 60));
            g.drawString("GAME OVER", 135, 170);
            g.drawString("请您重新开始游戏", 90, 280);
        }
    }

    //开始新游戏
    public void newGame() {
        //游戏清空重置
        gameReset();
        //修改状态值
        this.state = GAME_STATE_PLAYING;
        //加载游戏地图
        loadGameMap();
        //设置大本营
        camp.setState(Camp.CAMP_STATE_RUNNING);
        //创建坦克
        int ourTankX = 8 * Block.BLOCK_WIDTH;
        int ourTankY = (this.gameGradeMap.length - 2) * Block.BLOCK_HEIGHT;
        this.ourTank = new OurTank(ourTankX, ourTankY, Tank.UP);
        ourTank.work();
        //创建敌方坦克
        createEnemyTank();
        //启动线程来定时刷新画面
        refresh();
    }
    //加载游戏地图
    private void loadGameMap() {
        //得到本关地图
        this.gameGradeMap = GameMap.getGameMap(grade);
        //得到地图位置（几列几行）
        int row = this.gameGradeMap.length;
        int col = this.gameGradeMap[0].length;
        //循环
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                int mapValue = this.gameGradeMap[i][j];
                switch (mapValue) {
                    case Block.BLOCK_BRICK:
                        Brick brick = new Brick(j * Block.BLOCK_WIDTH, i * Block.BLOCK_HEIGHT);
                        this.blockList.add(brick);
                        break;
                    case Block.BLOCK_IRON:
                        Iron iron = new Iron(j * Block.BLOCK_WIDTH, i * Block.BLOCK_HEIGHT);
                        this.blockList.add(iron);
                        break;
                }
            }
        }
        //本关敌方坦克的数量
        this.enemyPoolMaxNum = GameMap.getEnemyTankCount(this.grade);
    }
    //游戏重置
    private void gameReset() {
        //停止上局正在运行的线程
        if (this.state == GAME_STATE_PLAYING) {
            this.state = GAME_STATE_INIT;
        }
        while (true) {
            if (this.enemyTankThreadIsRunning) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                break;
            }
        }
        //清空敌方坦克池中所有的坦克以及子弹
        for (int i = 0; i < enemyPool.size(); i++) {
            EnemyTank enemyTank = enemyPool.get(i);
            enemyTank.reSet();
            for (int j = 0; j < enemyTank.getBullets().size(); j++) {
                Bullet bullet = enemyTank.getBullets().get(j);
                bullet.reSet();
            }
            enemyTank.getBullets().clear();
        }
        enemyPool.clear();
        enemyDeadNum = 0;
        //清空我方坦克
        if (ourTank != null) {
            ourTank.reSet();
            for (int i = 0; i < ourTank.getBullets().size(); i++) {
                Bullet bullet = ourTank.getBullets().get(i);
                bullet.reSet();
            }
            ourTank.getBullets().clear();
        }
        //清空块池中所有的块
        blockList.clear();
    }
    //创建敌方坦克
    private void createEnemyTank() {
        //线程运行中
        this.enemyTankThreadIsRunning = true;
        //创建线程
        new Thread(() -> {
            //记录已投放的坦克数量
            int enemyTankNum = 0;
            try {
                while (this.state == GAME_STATE_PLAYING) {
                    //休眠一会
                    try {
                        //休眠时间短主要是考虑可以迅速退出本线程
                        Thread.sleep(repaintRate);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //控制连续投放间隔
                    long curCreateEnemyTankTime = System.currentTimeMillis();
                    //3s投放
                    if ((curCreateEnemyTankTime - this.lastEnemyTankTime) >= 3000) {
                        this.lastEnemyTankTime = curCreateEnemyTankTime;
                    } else {
                        continue;
                    }
                    //投放敌方坦克到达最大值后退出线程
                    if (enemyTankNum >= enemyPoolMaxNum) {
                        break;
                    }
                    //在敌方坦克池中寻找空闲的坦克随机显示在左上角或右上角
                    EnemyTank enemyTank = null;
                    for (int i = 0; i < enemyPool.size(); i++) {
                        EnemyTank tempTank = enemyPool.get(i);
                        if (tempTank.getState() == Tank.TANK_STOP) {
                            enemyTank = tempTank;
                            break;
                        }
                    }
                    //没有就增加一个
                    if (enemyTank == null) {
                        enemyTank = new EnemyTank(-100, -100, Tank.DOWN);
                        enemyPool.add(enemyTank);
                    }
                    //开始投放（随机投放到左上角与右上角，坦克不能重叠）
                    //敌方坦克X坐标
                    int enemyTankX;
                    //敌方坦克默认向下
                    int enemyDirection = Tank.DOWN;
                    //随机生成位置（0-左上角，1-右上角）
                    int randomPos = GameLogic.getRandomInt(0, 1);
                    //左上角（方向下/右）
                    if (randomPos == 0) {
                        enemyTankX = 0;
                        if (GameLogic.getRandomInt(0, 1) == 1) {
                            enemyDirection = Tank.RIGHT;
                        }
                    }
                    //右上角（方向下/左）
                    else {
                        enemyTankX = GAME_WIDTH - Tank.TANK_WIDTH;
                        if (GameLogic.getRandomInt(0, 1) == 1) {
                            enemyDirection = Tank.LEFT;
                        }
                    }
                    enemyTank.setX(enemyTankX);
                    enemyTank.setY(0);
                    enemyTank.setDirection(enemyDirection);
                    //判断该范围内是否可以投放运行
                    boolean workFlag = true;
                    for (int i = 0; i < enemyPool.size(); i++) {
                        EnemyTank tempTank = enemyPool.get(i);
                        if (tempTank.getState() == Tank.TANK_GO && enemyTank != tempTank) {
                            //敌方坦克占位，不能投放
                            if (GameLogic.getInstance().tankCollideTank(enemyTank, tempTank)) {
                                workFlag = false;
                                continue;
                            }
                        }
                    }
                    //我方坦克占位，不能投放
                    if (GameLogic.getInstance().tankCollideTank(enemyTank, ourTank)) {
                        workFlag = false;
                    }
                    //不能投放准备下一次
                    if (!workFlag) {
                        continue;
                    }
                    //可以投放--启动线程开始工作了
                    enemyTank.work();
                    enemyTankNum++;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //线程结束
                this.enemyTankThreadIsRunning = false;
            }
        }).start();
    }
    //游戏结束
    private void gameOver() {
        //设置游戏状态
        this.state = GAME_STATE_OVER;
        //游戏清空重置
        gameReset();
    }
    //游戏胜利
    private void gameWin() {
        if (enemyPoolMaxNum - enemyDeadNum <= 0) {
            //进 入 下 一 关
            this.grade++;
            newGame();
        }
    }
    //碰撞处理
    private void collide() {
        //判断我方子弹是否与敌方坦克或块或大本营碰撞
        for (int i = 0; i < ourTank.getBullets().size(); i++) {
            Bullet bullet = ourTank.getBullets().get(i);
            //我方子弹在飞行中
            if (bullet.getState() == Bullet.BULLET_STATE_RUNNING) {
                //判断是否与敌方坦克碰撞
                for (int j = 0; j < enemyPool.size(); j++) {
                    EnemyTank enemyTank = enemyPool.get(j);
                    //敌方坦克运行中
                    if (enemyTank.getState() == Tank.TANK_GO) {
                        //开始判断
                        if (GameLogic.getInstance().bulletCollideTank(bullet, enemyTank)) {
                            //记录坦克坐标，因为坦克销毁时会改变其坐标
                            int bombX = enemyTank.getX();
                            int bombY = enemyTank.getY();
                            //子弹销毁
                            bullet.reSet();
                            //坦克受到伤害
                            enemyTank.hurt(bullet);
                            //判断坦克是否死亡
                            if (enemyTank.isDead()) {
                                //坦克销毁
                                enemyTank.reSet();
                                enemyDeadNum++;
                                //坦克爆炸
                                addBomb(bombX, bombY, Tank.TANK_WIDTH, Tank.TANK_HEIGHT);
                                //判断是否胜利
                                gameWin();
                            }
                        }
                    }
                }
                //判断是否与块碰撞
                for (int j = blockList.size() - 1; j >= 0; j--) {
                    Block block = blockList.get(j);
                    //开始判断
                    if ((GameLogic.getInstance().bulletCollideBlock(bullet, block))) {
                        //记录块坐标
                        int bombX = block.getX();
                        int bombY = block.getY();
                        //子弹销毁
                        bullet.reSet();
                        //块被摧毁
                        if (block.getType() == Block.BLOCK_BRICK) {
                            //砖块被销毁
                            blockList.remove(j);
                            //砖块爆炸
                            addBomb(bombX, bombY, Block.BLOCK_WIDTH, Block.BLOCK_HEIGHT);
                        } else {
                            addBomb(bombX, bombY, Block.BLOCK_WIDTH, Block.BLOCK_HEIGHT);
                        }
                    }
                }
                //判断是否与大本营碰撞
                if (camp.getState() == Camp.CAMP_STATE_RUNNING) {
                    if (GameLogic.getInstance().bulletCollideCamp(bullet, camp)) {
                        //子弹销毁
                        bullet.reSet();
                        //大本营被摧毁
                        camp.setState(Camp.CAMP_STATE_FREE);
                        //大本营爆炸
                        addBomb(camp.getX(), camp.getY(), camp.getWidth(), camp.getHeight());
                        //游戏结束
                        gameOver();
                    }
                }
            }
        }
        //判断敌方子弹是否与我方坦克或块或大本营碰撞（不需要判断敌方坦克状态，因为即使坦克销毁子弹可能仍然在飞）
        for (int i = 0; i < enemyPool.size(); i++) {
            //判断是否与我方坦克碰撞
            EnemyTank enemyTank = enemyPool.get(i);
            for (int j = 0; j < enemyTank.getBullets().size(); j++) {
                Bullet bullet = enemyTank.getBullets().get(j);
                if (bullet.getState() == Bullet.BULLET_STATE_RUNNING) {
                    //判断子弹是否与坦克碰撞
                    if (GameLogic.getInstance().bulletCollideTank(bullet, ourTank)) {
                        //记录坦克坐标，因为坦克销毁时会改变其坐标
                        int bombX = ourTank.getX();
                        int bombY = ourTank.getY();
                        //子弹销毁
                        bullet.reSet();
                        //坦克受到伤害
                        ourTank.hurt(bullet);
                        //判断坦克是否死亡
                        if (ourTank.isDead()) {
                            //坦克销毁
                            ourTank.reSet();
                            //坦克爆炸
                            addBomb(bombX, bombY, Tank.TANK_WIDTH, Tank.TANK_HEIGHT);
                            //游戏结束
                            gameOver();
                        }
                    }
                    //判断子弹是否与块碰撞
                    for (int k = blockList.size() - 1; k >= 0; k--) {
                        Block block = blockList.get(k);
                        //开始判断
                        if ((GameLogic.getInstance().bulletCollideBlock(bullet, block))) {
                            //记录块坐标
                            int bombX = block.getX();
                            int bombY = block.getY();
                            //子弹销毁
                            bullet.reSet();
                            //块被摧毁
                            if (block.getType() == Block.BLOCK_BRICK) {
                                //砖块被销毁
                                blockList.remove(k);
                                //砖块爆炸
                                addBomb(bombX, bombY, Block.BLOCK_WIDTH, Block.BLOCK_HEIGHT);
                            } else {
                                addBomb(bombX, bombY, Block.BLOCK_WIDTH, Block.BLOCK_HEIGHT);
                            }
                        }
                    }
                    //判断子弹是否与大本营碰撞
                    if (camp.getState() == Camp.CAMP_STATE_RUNNING) {
                        if (GameLogic.getInstance().bulletCollideCamp(bullet, camp)) {
                            //子弹销毁
                            bullet.reSet();
                            //大本营被摧毁
                            camp.setState(Camp.CAMP_STATE_FREE);
                            //大本营爆炸
                            addBomb(camp.getX(), camp.getY(), camp.getWidth(), camp.getHeight());
                            //游戏结束
                            gameOver();
                        }
                    }
                }
            }
        }
    }

    //添加爆炸
    private void addBomb(int x, int y, int w, int h) {
        //查找空闲操作
        Bomb bomb = null;
        for (int i = 0; i < bombList.size(); i++) {
            Bomb tempBomb = bombList.get(i);
            //判断是否空闲
            if (tempBomb.getState() == Bomb.BOMB_STATE_FREE) {
                bomb = tempBomb;
                break;
            }
        }
        //没有就增加一个
        if (bomb == null) {
            bomb = new Bomb(-100, -100);
            bombList.add(bomb);
        }
        bomb.setX(x);
        bomb.setY(y);
        bomb.setWidth(w);
        bomb.setHeight(h);
        bomb.prepareBomb();
    }
    /**
     * 利用线程来定时刷新重绘游戏画面，适用于飞机类刷新频率较高的游戏。
     * 动画原理就是在极短时间内连续显示多个图片，给人眼一种画面动起来的错觉（人眼的识别静态图时间为0.1秒）。
     * 屏幕刷新频率（FPS->帧/每秒），单位赫兹（Hz）。
     * 20Hz ->每秒刷新画面20次，即每隔（1000/20）毫秒刷新一次画面。
     */
    //游戏面板的刷新
    public void refresh() {
        //线程-->利用Lambda表达式替代内部类更方便
        new Thread(() -> {
            while (this.state == GAME_STATE_PLAYING) {
                //碰撞处理
                collide();
                //每隔50毫秒重新绘图,1s刷新20次
                repaint();
                try {
                    Thread.sleep(repaintRate);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyPressed(KeyEvent e) {
        //游戏未开始或结束禁止按键
        if (this.state != GAME_STATE_PLAYING) {
            return;
        }
        int keyCode = e.getKeyCode();
        //38->上  40->下  37->左  39->右 65->a：攻击键位
        //System.out.println("键盘按下"+keyCode);
        switch (keyCode) {
            case KeyEvent.VK_W:
            case KeyEvent.VK_UP:
                ourTank.move(Tank.UP);
                break;
            case KeyEvent.VK_D:
            case KeyEvent.VK_RIGHT:
                ourTank.move(Tank.RIGHT);
                break;
            case KeyEvent.VK_S:
            case KeyEvent.VK_DOWN:
                ourTank.move(Tank.DOWN);
                break;
            case KeyEvent.VK_A:
            case KeyEvent.VK_LEFT:
                ourTank.move(Tank.LEFT);
                break;
            case KeyEvent.VK_SPACE:
                ourTank.fire();
                break;
        }

    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

    public Vector<EnemyTank> getEnemyPool() {
        return enemyPool;
    }

    public OurTank getOurTank() {
        return ourTank;
    }

    public Camp getCamp() {
        return camp;
    }

    public List<Block> getBlockList() {
        return blockList;
    }

    public void setGrade(int grade) {
        this.grade = grade;
    }
}
